let data = {
  users: [{
    id: 1,
    name: 'zhangsan'
  }, {
    id: 2,
    name: 'lisi'
  }],
  home: {
    city: '北京'
  }
};
class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}
class Watcher {
  constructor(vm, exprOrFn, cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = getEvaluate(exprOrFn, vm);
    Dep.target = this;
    this.oldValue = this.getter();//data.users
    Dep.target = null;
  }
  update() {
    const newValue = this.getter();
    this.cb(newValue, this.oldValue);
    this.oldValue = newValue;
  }
}
function getEvaluate(expression, vm) {//执行此函数也就等同于访问 data.users
   return new Function(`with(this){return ` + expression + `}`).bind(vm);
}
function observe(value) {
  if (!value || typeof value !== 'object') {
    return;
  }
  if (Array.isArray(value)) {
    observeArray(value);
  } else {
    walk(value);
  }
}
function observeArray(value) {
  value.forEach(item => observe(item));
}
function walk(value) {
  for (let key in value) {
    defineReactive(value, key);
  }
}
function defineReactive(obj, key, value = obj[key]) {
  observe(value);
  let dep = new Dep();
  if(Array.isArray(value)){
    //缓存老的push方法
    let originPush = value.push;
    //重写数组的push方法
    value.push =  function(){
        //调用老的push方法添加元素
        originPush.apply(this,arguments);
        //当新元素添加完成后，通知当前依赖的watcher进行更新
        dep.notify();
    }
  }
  Object.defineProperty(obj, key, {
    get() {
      console.log('key', key);
      if (Dep.target) {//users这个key对应的Dep实例上多了一个Watcher
        dep.addSub(Dep.target);
      }
      return value;
    },
    set(newVal) {//现在只有在set users的时候才会进入更新通知
      if (newVal !== value) {
        value = newVal;//以后如果修改了users的话，那会进入 setter,进行依赖的更新通知
        dep.notify();//通知此依赖的订阅者/观察者
      }
    }
  });
}
observe(data);
//添加一个观察者，观察data的users属性变化，当发生变化后执行回调函数
new Watcher(data,'users',()=>{
    console.log('检测到users变更了');
});
//data.users = [{id: 3,name: 'wangwu'}];
//现在当用户调用数组的 push方法的时候进行依赖更新通知 
data.users.push({id:3,name:'wangwu'});